Skip to main content

JavaScript Execution Context - Complete Guide

Table of Contentsโ€‹

  1. What is an Execution Context?
  2. Types of Execution Context
  3. Global Execution Context (GEC)
  4. Function Execution Context (FEC)
  5. Call Stack
  6. The this Keyword in Execution Context
  7. Lexical vs Variable Environment
  8. Hoisting Summary
  9. Complete Execution Flow Example
  10. Execution Context vs Scope
  11. Closures and Execution Context
  12. Execution Context and Event Loop
  13. Microtasks and Execution Context
  14. Common Interview Questions
  15. Mental Models

What is an Execution Context?โ€‹

An Execution Context (EC) is the environment where JavaScript code is evaluated and executed. Think of it as a container that holds all the information needed to run a piece of code.

Every Execution Context has three components:

  • Variable Environment - stores variables declared with var
  • Lexical Environment - stores variables declared with let and const
  • this binding - determines what this refers to

Types of Execution Contextโ€‹

There are three types of execution contexts in JavaScript:

  1. Global Execution Context (GEC) - created when the JavaScript file starts executing
  2. Function Execution Context (FEC) - created each time a function is invoked
  3. Eval Execution Context - created when code runs inside eval() (rarely used in modern JavaScript)

Global Execution Context (GEC)โ€‹

When is it created?โ€‹

The Global Execution Context is created when your JavaScript file starts executing. There is only one GEC in your entire program, and it's stored at the bottom of the Call Stack.

What happens inside the Global EC?โ€‹

The GEC goes through two phases:

1. Memory Creation Phase (Hoisting)โ€‹

During this phase, JavaScript allocates memory for variables and functions:

  • var declarations โ†’ initialized to undefined
  • let and const โ†’ placed in the Temporal Dead Zone (TDZ)
  • Function declarations โ†’ fully hoisted with their definitions
  • this โ†’ points to window (browser) or global (Node.js)
console.log(a); // undefined (hoisted but not yet assigned)
var a = 10;

2. Execution Phaseโ€‹

During this phase, the code runs line by line:

  • Variables are assigned their actual values
  • Functions are invoked
  • Expressions are evaluated

Function Execution Context (FEC)โ€‹

When is it created?โ€‹

A new Function Execution Context is created each time a function is invoked. When the function completes, its execution context is destroyed.

What happens inside the Function EC?โ€‹

Like the GEC, the FEC also has two phases:

Memory Creation Phaseโ€‹

  • Function parameters โ†’ initialized to undefined
  • Local variables โ†’ hoisted according to their declaration type
  • Inner functions โ†’ hoisted
  • this โ†’ depends on how the function is called

Execution Phaseโ€‹

  • Parameters and variables are assigned values
  • Statements are executed
  • Return value is computed

Call Stackโ€‹

The Call Stack is a data structure that tracks execution contexts. It follows the Last In, First Out (LIFO) principle.

Example:โ€‹

function one() {
two();
}

function two() {
console.log('Hello');
}

one();

Stack Flow:โ€‹

1. Global EC (pushed)
2. one() EC (pushed)
3. two() EC (pushed)
4. two() EC (popped - after console.log)
5. one() EC (popped - after two() returns)
6. Global EC remains until program ends

Visual Representation:โ€‹

Call Stack at peak:

| two() | โ† Top
| one() |
| GEC | โ† Bottom

The this Keyword in Execution Contextโ€‹

The value of this depends on the execution context:

In Global EC:โ€‹

console.log(this); // window (browser) / global (Node.js)

In Function EC:โ€‹

function test() {
console.log(this);
}
test(); // window (non-strict mode), undefined (strict mode)

In Method Call:โ€‹

const obj = {
fn: function() {
console.log(this);
}
};
obj.fn(); // this โ†’ obj

Lexical vs Variable Environmentโ€‹

Both are part of the execution context but handle different types of declarations:

Lexical Environmentโ€‹

  • Stores variables declared with let and const
  • Block-scoped
  • Respects block boundaries ({})

Variable Environmentโ€‹

  • Stores variables declared with var
  • Function-scoped
  • Ignores block boundaries (except function blocks)

Hoisting Summaryโ€‹

DeclarationHoisted?Initial Value
varโœ… Yesundefined
letโœ… YesTDZ (Temporal Dead Zone)
constโœ… YesTDZ (Temporal Dead Zone)
functionโœ… YesFunction reference
Arrow functionโŒ NoTDZ

Complete Execution Flow Exampleโ€‹

Let's trace through a complete example:

var x = 1;

function foo(a) {
var y = 2;
function bar() {
console.log(a, x, y);
}
bar();
}

foo(10);

Execution Order:โ€‹

  1. Global EC created

    • x hoisted โ†’ undefined
    • foo hoisted โ†’ function reference
  2. Execution phase begins

    • x assigned 1
  3. foo(10) called โ†’ Function EC created

    • a assigned 10
    • y hoisted โ†’ undefined
    • bar hoisted โ†’ function reference
    • y assigned 2
  4. bar() called โ†’ New Function EC created

    • Looks up a โ†’ finds 10 in foo's scope
    • Looks up x โ†’ finds 1 in global scope
    • Looks up y โ†’ finds 2 in foo's scope
    • Logs: 10 1 2
  5. Contexts are popped in reverse order


Execution Context vs Scopeโ€‹

These concepts are often confused but are fundamentally different:

AspectExecution ContextScope
DefinitionEnvironment where code runsWhere variables are accessible
CreatedAt runtimeAt definition time (lexical)
StorageCall StackLexical structure
LifetimeDestroyed after executionCan survive via closures

One-liner:โ€‹

Execution context is runtime, scope is lexical.

Example:โ€‹

function outer() {
var x = 10;

function inner() {
console.log(x);
}

return inner;
}

const fn = outer();
fn(); // 10

In this example:

  • outer()'s execution context is destroyed โŒ
  • But x is still accessible โœ… โ†’ this is a closure
  • Scope survives EC destruction

Closures and Execution Contextโ€‹

Definitionโ€‹

A closure is created when a function remembers variables from its lexical scope, even after the outer function's execution context is gone.

How Closures Form (Internals)โ€‹

function counter() {
let count = 0;

return function() {
count++;
console.log(count);
};
}

const inc = counter();
inc(); // 1
inc(); // 2

What Happens Internally:โ€‹

  1. counter() โ†’ Function EC created
  2. count stored in Lexical Environment
  3. Inner function returned
  4. counter() EC popped from Call Stack
  5. count retained via closure (reference maintained)

Memory Model:โ€‹

Heap:
โ””โ”€โ”€ LexicalEnv { count: 0 }

Call Stack:
โ””โ”€โ”€ inner() EC

Important Points:โ€‹

  • Closures don't copy values โŒ
  • Closures keep references โœ…
  • Common use cases:
    • Event handlers
    • setTimeout
    • Currying
    • Memoization

Execution Context and Event Loopโ€‹

Understanding how execution contexts interact with the event loop is crucial:

console.log('A');

setTimeout(() => console.log('B'), 0);

console.log('C');

Execution Flow:โ€‹

  1. Global EC created
  2. 'A' logged
  3. setTimeout โ†’ callback sent to Web APIs
  4. 'C' logged
  5. Global EC completes
  6. Event loop pushes callback to Call Stack
  7. 'B' logged

Output:โ€‹

A
C
B

Key Rule:โ€‹

The Call Stack must be empty before the Event Loop can push callbacks from the queue.


Microtasks and Execution Contextโ€‹

console.log('A');

Promise.resolve().then(() => console.log('B'));

console.log('C');

Output:โ€‹

A
C
B

Why?โ€‹

Microtasks (like Promise.then) are executed:

  • After the current execution context finishes
  • Before macrotasks (like setTimeout)

Priority Order:โ€‹

1. Call Stack
2. Microtask Queue (Promises, queueMicrotask)
3. Macrotask Queue (setTimeout, setInterval, I/O)

Common Interview Questionsโ€‹

Q: Does closure increase memory usage?โ€‹

A: Yes, if references are retained unnecessarily. Closures keep variables in memory even after the outer function completes.

Q: Is a closure created on function call or definition?โ€‹

A: Definition time (lexical scope). The closure is formed when the function is defined, not when it's called.

Q: Does a block create an execution context?โ€‹

A: No โŒ. Only functions create execution contexts. Blocks create lexical scope but not execution contexts.


Mental Modelsโ€‹

ConceptThink of it as
Execution ContextStack frame in memory
ScopeVariable visibility rules
ClosurePreserved lexical environment
Event LoopStack coordinator

Interview One-Linersโ€‹

Execution Context: "An execution context is the environment where JavaScript code runs, consisting of memory creation and execution phases, created globally once and per function call thereafter."

EC vs Scope vs Closure: "Execution contexts manage runtime execution, scopes define variable access, and closures allow lexical environments to survive beyond their execution contexts."


Execution Context Lifecycleโ€‹

Code runs
โ†“
Execution Context created
โ†“
Memory phase (hoisting)
โ†“
Execution phase
โ†“
Context popped from stack
โ†“
Next context runs (if any)

Key Takeawaysโ€‹

  1. Execution contexts are created at runtime and manage code execution
  2. Scope is determined at definition time and controls variable access
  3. Closures allow inner functions to access outer variables even after the outer function has finished
  4. The Call Stack tracks all active execution contexts
  5. The Event Loop coordinates between the Call Stack and task queues
  6. Microtasks have higher priority than macrotasks